/*
	jGrouse framework
	Copyright 2007 by Denis Riabtchik 
	All rights reserved.
	
	Licensed under modified BSD license. See license.txt for details
*/

/**
	Different implementations of Set functionality
	@module jgrouse.util.Sets
	@requires module-jgrouse.base
	@requires module-jgrouse.coreutil
*/
jgrouse.module(
{
	name : 'jgrouse.util.Sets',
	requires : ['jgrouse.base', 'jgrouse.coreutil'],
	body : function(embed)
	{
		eval(embed);
		
		/**
			Abstract set - essentially collection that does not contain duplicate elements.
			@class jgrouse.util.Set
			@extends jgrouse.BaseObject
		*/
		jgrouse.define('jgrouse.util.Set', 'jgrouse.BaseObject', function(salt)
		{
			eval(salt);
			
			return {
				/**
					@constructor initialize
				*/
				initialize : function()
				{
					_super(this, 'initialize');
				},
				
				/**
					Put content of array into set
					@function putArray
					@param {array} values - array of values that should be put into the set
				*/
				putArray : function(values)
				{
					var athis = this;
					jgrouse.forEach(values, function(item)
					{
						athis.put(item);
					});
				},
				
				/**
					Puts item into set.
					@function put
					@param {object} value - value to add to set
				*/
				put : function(value)
				{
					jgrouse.notImplemented(this.RTTI + '.put');
				},
				
				/**
					Checks if given item exists in set.
					@function {boolean} exists
					@param {object} item - item to check for existence in the set
					@returns true if item was found 
				*/
				exists : function(item)
				{
					jgrouse.notImplemented(this.RTTI + '.exists');
				},
				
				/**
					Converts set to array
					@function {array} toArray
					@returns array containing all elements of set
				*/
				toArray : function()
				{
					jgrouse.notImplemented(this.RTTI + '.toArray');
				},
				
				/**
					Remove element from set
					@function remove
					@param {object} item - item to be removed
				*/
				remove : function(item)
				{
					jgrouse.notImplemented(this.RTTI + '.remove');
				},
				
				/**
					Invoke given function for each member of the set
					@function forEach
					@param {function(item, index)} iterator - function to be called for each element of the set
					@param {boolean} backwards - if iteration should start from the end (optional, if applicable)
				*/
				forEach : function(iterator, backwards)
				{
					jgrouse.notImplemented(this.RTTI + '.forEach');
				},
				
				/**
					Remove all elements from the set
					@function clear
				*/
				clear : function()
				{
					jgrouse.notImplemented(this.RTTI + '.clear');
				}				
			}
		});
		
		/**
			Set of primitive elements (numbers, strings, etc).
			@class jgrouse.util.PrimitiveSet
			@extends jgrouse.util.Set
		*/
		jgrouse.define('jgrouse.util.PrimitiveSet', 'jgrouse.util.Set', function(salt)
		{
			eval(salt);
			
			return {
				/**
					@constructor initialize
				*/
				initialize : function()
				{
					_super(this, 'initialize');
					this._data = {};
				},
				
				/**
					@function put
					@inheritdesc
				*/
				put : function(value)
				{
					this._data[value] = true;
				},
				
				/**
					@function exists
					@inheritdesc
				*/
				exists : function(item)
				{
					return this._data[item];
				},
				
				/**
					@function toArray
					@inheritdesc
				*/
				toArray : function()
				{
					var arr = [];
					jgrouse.forEach(this._data, function(value, index)
					{
						arr.push(index);
					});
					return arr;
				},
				
				/**
					@function remove
					@inheritdesc
				*/
				remove : function(item)
				{
					delete this._data[item];
					return item;
				},
				
				/**
					@function forEach
					@inheritdesc
				*/
				forEach : function(iterator, backwards)
				{
					var index = 0;
					jgrouse.forEach(this._data, function(value, item)
					{
						iterator(item, index++);
					});
				},
				
				/**
					@function clear
					@inheritdesc
				*/
				clear : function()
				{
					this._data = [];
				}				
			}
		});
		
		
		/**
			Array-based set. This class either relies on supplied comparator function to tell
			if given objects are equal or considers two objects to be the same if and only if they are pointing to 
			the same instance
			@class jgrouse.util.ObjectSet
			@extends jgrouse.util.Set
		*/
		jgrouse.define('jgrouse.util.ObjectSet', 'jgrouse.util.Set', function(salt)
		{
			eval(salt);
			
			return {
				/**
					@constructor initialize
					@param {function(obj1, obj2)} comparator - function that is invoked to check if the passed two 
					objects are equal. Optional 
				*/
				initialize : function(comparator)
				{
					_super(this, 'initialize');
					this._data = [];
					this._comparator = comparator? comparator : this.__defComparator;
				},
				
				__defComparator : function(item1, item2)
				{
					return item1 === item2;
				},
				
				_search : function(item)
				{
					return jgrouse.search(this._data, item, this._comparator);
				},
				
				
				/**
					@function put
					@inheritdesc
				*/
				put : function(value)
				{
					var index = this._search(value);
					if (index < 0)
					{
						this._data.push(value);
					}
				},
				
				/**
					@function exists
					@inheritdesc
				*/
				exists : function(item)
				{
					return this._search(item) >= 0;
				},
				
				/**
					@function toArray
					@inheritdesc
				*/
				toArray : function()
				{
					return [].concat(this._data);
				},
				
				/**
					@function remove
					@inheritdesc
				*/
				remove : function(item)
				{
					var i = this._search(item);
					if (i >= 0)
					{
						this._data.splice(i, 1);
					}
					return item;
				},
				
				/**
					@function forEach
					@inheritdesc
				*/
				forEach : function(iterator, backwards)
				{
					jgrouse.forEach(this._data, iterator, backwards);
				},

				/**
					@function clear
					@inheritdesc
				*/				
				clear : function()
				{
					this._data = [];
				}
			}
		});
	}
});